﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Text;
using System.Windows.Forms;

using ZedGraph;

namespace StockLearner
{
    public partial class Form1 : Form
    {
        YahooResponseParser m_responseParser;
        int m_curPosition = 30;
        NNAgent m_agent;
        List<Double> m_agentPredictionXs;
        List<Double> m_agentPredictionYs;
        Queue<List<YahooDataPoint>> m_predictionQueue;
        Queue<List<YahooDataPoint>> m_stateQueue;
        List<Double> m_rewards;

        public Form1()
        {
            InitializeComponent();
            m_responseParser = new YahooResponseParser();
            m_agent = new NNAgent();
            m_agentPredictionXs = new List<Double>();
            m_agentPredictionYs = new List<Double>();
            m_predictionQueue = new Queue<List<YahooDataPoint>>();
            m_stateQueue = new Queue<List<YahooDataPoint>>();
            m_rewards = new List<double>();
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void GetPriceBtn_Click(object sender, EventArgs e)
        {
            UpdateCharts(true);
            ResetAgent();
        }

        private void UpdateCharts(bool p_regrabTickerData)
        {
            if (p_regrabTickerData)
            {
                String l_url_string = "http://chartapi.finance.yahoo.com/instrument/1.0/" + this.textBox1.Text + "/chartdata;type=quote;range=1d/csv";

                string l_downloadedString;
                System.Net.WebClient l_client;

                l_client = new System.Net.WebClient();

                l_downloadedString = l_client.DownloadString(l_url_string);
                m_responseParser.parseDataString(l_downloadedString);
            }

            this.priceGraph.GraphPane.CurveList.Clear();
            this.volumeGraph.GraphPane.CurveList.Clear();

            GraphPane l_pricepane = this.priceGraph.GraphPane;
            GraphPane l_volpane = this.volumeGraph.GraphPane;

            l_pricepane.XAxis.Type = AxisType.Date;
            l_pricepane.XAxis.Scale.MajorUnit = DateUnit.Day;
            l_pricepane.XAxis.Scale.Format = "T";
            l_volpane.XAxis.Type = AxisType.Date;
            l_volpane.XAxis.Scale.MajorUnit = DateUnit.Day;
            l_volpane.XAxis.Scale.Format = "T";

            double[] l_timestamps = m_responseParser.getTimestamps();
            double[] l_oaTimestamps = new double[l_timestamps.Length];

            for (int i = 0; i < l_timestamps.Length; i++)
            {
                l_oaTimestamps[i] = Utils.UnixTimestampToXdate(l_timestamps[i]);
            }
            double[] l_predXs = m_agentPredictionXs.ToArray();
            PointPairList l_spl1 = new PointPairList(l_oaTimestamps, m_responseParser.getPrices());
            PointPairList l_spl2 = new PointPairList(l_oaTimestamps, m_responseParser.getVolumes());
            PointPairList l_spl3 = new PointPairList(l_predXs, m_agentPredictionYs.ToArray());
            for (int i = 0; i < l_predXs.Length; i++)
            {
                l_predXs[i] = l_predXs[i] - (30.0 / (60.0 * 24.0));
            }
            PointPairList l_spl4 = new PointPairList(l_predXs, m_agentPredictionYs.ToArray());

            LineItem l_curve1 = l_pricepane.AddCurve("Avg Price", l_spl1, Color.Blue, SymbolType.None);
            LineItem l_curve2 = l_volpane.AddCurve("Volume", l_spl2, Color.Red, SymbolType.None);
            LineItem l_curve3 = l_pricepane.AddCurve("Pred Price (delay)", l_spl3, Color.Purple, SymbolType.None);
            //LineItem l_curve4 = l_pricepane.AddCurve("Pred Price (RT)", l_spl4, Color.Green, SymbolType.None);

            l_curve1.Line.Width = 3.0f;
            l_curve2.Line.Width = 3.0f;
            l_curve3.Line.Width = 3.0f;
            //l_curve4.Line.Width = 3.0f;

            this.priceGraph.AxisChange();
            this.priceGraph.Refresh();
            this.volumeGraph.AxisChange();
            this.volumeGraph.Refresh();
        }

        private void AgentResetBtn_Click(object sender, EventArgs e)
        {
            ResetAgent();
        }

        private void ResetAgent()
        {
            m_curPosition = 30;
            CurAgentTimeBox.Text = Utils.UnixTimeStampToDateTime(m_responseParser.Data[m_curPosition].Timestamp).ToString();
            m_agentPredictionXs.Clear();
            m_agentPredictionYs.Clear();
            m_predictionQueue.Clear();
            m_stateQueue.Clear();

            m_agent.resetRewardSum();
        }

        private void ActionBtn_Click(object sender, EventArgs e)
        {
            Double l_stepsToTake = Double.Parse(this.AgentTimeStepBox.Text);

            int i = 0;

            while (i < l_stepsToTake)
            {
                bool l_success = TakeStep(true);
                if (l_success)
                {
                    ++i;
                }
                else
                {
                    i = ((int)l_stepsToTake) + 1;
                }
            }
        }

        public static List<YahooDataPoint> GeneralizeAndDiscretize(List<YahooDataPoint> p_specificState)
        {
            List<YahooDataPoint> l_toReturn = new List<YahooDataPoint>();
            double l_startClose = p_specificState[0].Close;
            double l_startOpen = p_specificState[0].Open;
            double l_startHigh = p_specificState[0].High;
            double l_startLow = p_specificState[0].Low;
            double l_startVolume = p_specificState[0].Volume;

            for (int i = 0; i < p_specificState.Count; i++)
            {
                YahooDataPoint l_toAdd = new YahooDataPoint();
                l_toAdd.Timestamp = p_specificState[i].Timestamp;
                l_toAdd.Close = p_specificState[i].Close / l_startClose;
                l_toAdd.Open = p_specificState[i].Open / l_startOpen;
                l_toAdd.Low = p_specificState[i].Low / l_startLow;
                l_toAdd.High = p_specificState[i].High / l_startHigh;
                l_toAdd.Volume = (p_specificState[i].Volume / l_startVolume);

                l_toAdd.Close = Math.Round(l_toAdd.Close, 3);
                l_toAdd.Open = Math.Round(l_toAdd.Open, 3);
                l_toAdd.Low = Math.Round(l_toAdd.Low, 3);
                l_toAdd.High = Math.Round(l_toAdd.High, 3);
                l_toAdd.Volume = Math.Round(l_toAdd.Volume, 3);

                l_toReturn.Add(l_toAdd);
            }

            return l_toReturn;
        }

        private bool TakeStep(bool p_redraw)
        {
            ++m_curPosition;
            if (m_curPosition < m_responseParser.Data.Count)
            {
                int l_unixTS = m_responseParser.Data[m_curPosition].Timestamp;
                CurAgentTimeBox.Text = Utils.UnixTimeStampToDateTime(l_unixTS).ToString();
                List<YahooDataPoint> l_state = m_responseParser.Data.GetRange(m_curPosition - 30, 30);
                List<YahooDataPoint> l_prediction = m_agent.act(GeneralizeAndDiscretize(l_state));

                m_stateQueue.Enqueue(Utils.DeepCpyYDPList(l_state));
                m_predictionQueue.Enqueue(Utils.DeepCpyYDPList(l_prediction));

                double l_generalizationValue = l_state[0].Close;
                int l_unixTSPred = 0;

                for (int i = 0; i < l_prediction.Count; i++)
                {
                    l_unixTSPred = l_prediction[i].Timestamp;
                    m_agentPredictionXs.Add(Utils.ConvertDateToXdate(Utils.UnixTimeStampToDateTime(l_unixTSPred)));
                    m_agentPredictionYs.Add(l_prediction[i].Close * l_generalizationValue);
                }
                
                if(p_redraw)
                    UpdateCharts(false);

                int l_topTimestamp = m_predictionQueue.Peek()[0].Timestamp;

                if (l_topTimestamp < l_unixTS)
                {
                    List<YahooDataPoint> l_predToEval = m_predictionQueue.Dequeue();
                    List<YahooDataPoint> l_stateAtPredToEval = m_stateQueue.Dequeue();
                    List<YahooDataPoint> l_statePrime = Utils.DeepCpyYDPList((l_state));
                    m_agent.processReward(l_predToEval, l_stateAtPredToEval, l_statePrime);
                }
            }
            else
            {
                //MessageBox.Show("Not enough data for a full state representation at the current timestep.\nReset agent and try again.");
                return false;
            }

            return true;
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            m_agent.saveKnowledge();
        }

        private void RunContBtn_Click(object sender, EventArgs e)
        {
            Double l_stepsToTake = Double.Parse(this.AgentTimeStepBox.Text);

            int i = 0;
            System.IO.StreamWriter l_file = new System.IO.StreamWriter(@"Learning.csv", false);

            while (i < l_stepsToTake)
            {
                bool l_success = TakeStep(false);
                int l_loops = 1;
                while (l_success)
                {
                    if (l_loops++ < 30)
                        l_success = TakeStep(false);
                    else
                    {
                        l_success = TakeStep(true);
                        l_loops = 0;
                    }
                }
                m_rewards.Add(m_agent.getRewardSum());
                l_file.WriteLine(i + "," + m_agent.getRewardSum());
                ResetAgent();
                ++i;
            }
            l_file.Close();
        }
    }
}
